<template>
    <el-button @click="save">Save</el-button>
    <el-button @click="loadFromLocalStorage">loadFromLocalStorage</el-button>
    <el-button @click="newTrack('straight')">添加直轨</el-button>
    <el-button @click="newTrack('arc_1_6')">添加1/6弯轨</el-button>
    <!-- <el-button @click="add">loadFromLocalStorage</el-button> -->
    <div id="menu" class="menu-x" ref="menu" :style="{
        'visibility': menuStyle.visible ? 'visible' : 'hidden',
        'top': menuStyle.top + 'px',
        'left': menuStyle.left + 'px',
        'z-index': 100
    }">
        <div class="menu-li">什么都不做</div>
        <div class="menu-li">什么都不做</div>
        <div class="menu-li">什么都不做</div>
        <div class="menu-li">什么都不做</div>
        <div class="menu-li" @click="delEl()">删除</div>
    </div>

    <canvas id="c"></canvas>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue"
import { StaticCanvas, FabricText, Canvas, Rect, util, Path, Group, FabricObject } from 'fabric';
import { initAligningGuidelines, normalizeAngle, End } from "./utils"
import { ElNotification } from "element-plus";


Rect.stateProperties.push('ends')
Group.stateProperties.push('ends')
const menu = ref<HTMLDivElement>()
const menuStyle = ref({
    top: -1,
    left: -1,
    visible: false,
})
let contextMenuTarget: FabricObject | null = null
let canvas: Canvas | null = null
const trackWidth = 20
const save = () => {
    if (canvas != null) {
        const serializedData = canvas.toJSON()
        console.log(serializedData)
        localStorage.setItem('track', JSON.stringify(serializedData))
    } else {
        ElNotification.error({
            title: 'Error',
            message: 'Canvas is null',
            type: 'error'
        })
    }
}
const loadFromLocalStorage = () => {
    const serializedData = localStorage.getItem('track')
    if (serializedData != null) {
        if (canvas == null) {
            return
        }
        canvas.clear()
        console.log(serializedData)
        const data = JSON.parse(serializedData)
        // canvas = new Canvas('c')
        canvas.loadFromJSON(data)
    } else {
        ElNotification.error({
            title: 'Error',
            message: 'No data in localStorage',
        })
    }
}

const newTrack = (type: 'straight' | 'arc_1_6') => {
    if (type == 'straight') {
        createStraightTrack(canvas!)
    } else if (type == 'arc_1_6') {
        createArcTrack(canvas!, 0, 30)
    }
}
const currentIDs = {
    switch: 0,
    newSwitchID(): { switchID: string, mainTrackID: string, sideTrackIDLarge: string, sideTrackIDSmall: string } {
        const switchIDNum = currentIDs.switch++
        const switchID = `switch-${switchIDNum}`
        return this.getSwitchIDGroup(switchID)
    },
    getSwitchIDGroup(switchID: string): { switchID: string, mainTrackID: string, sideTrackIDLarge: string, sideTrackIDSmall: string } {
        const mainTrackID = `${switchID}-main`
        const sideTrackIDLarge = `${switchID}-side-large`
        const sideTrackIDSmall = `${switchID}-side-small`
        return {
            switchID,
            mainTrackID,
            sideTrackIDLarge,
            sideTrackIDSmall
        }
    }
}
//  创建道岔
const createSwitch = (canvas: Canvas, angle: number, radiusLarge: number, radiusSmall: number, side: 'right' | 'left') => {
    // const endAngle = 80
    const { largeArc, smallArc } = createArcElements(canvas, angle, radiusSmall, side, { x: 0, y: 100 });
    const straightTrackLength = 120
    // 道岔从上到下创建。
    var rect: Rect
    var ends: End[] = []
    const centerLineRadius = (radiusLarge + radiusSmall) / 2
    if (side == 'left') {
        let switchCurveCenterHeight = radiusLarge * Math.abs(Math.sin(util.degreesToRadians(angle)))
        // console.log((straightTrackLength- switchCurveCenterHeight), switchCurveCenterHeight, angle)
        rect = new Rect({
            left: radiusLarge,
            top: -(straightTrackLength - switchCurveCenterHeight),// switchCurveCenterHeight,
            fill: '#19e157',
            width: straightTrackLength,
            height: 20,
            angle: 90
        });
        // Group之后，会生成一个包含直道和弯道的Group，其高度应当取直道和弯道中间较大值
        switchCurveCenterHeight = Math.max(switchCurveCenterHeight, straightTrackLength)
        ends = [
            // 岔道共同端点    
            {
                heading: 180,
                dx: centerLineRadius,
                dy: switchCurveCenterHeight
            },
            // 直道端点
            {
                heading: 0,
                dx: centerLineRadius,
                dy: (switchCurveCenterHeight - straightTrackLength)
            },
            // 弯道端点
            {
                heading: 0 - angle,
                dx: centerLineRadius * Math.cos(util.degreesToRadians(angle)),
                dy: switchCurveCenterHeight - centerLineRadius * Math.sin(util.degreesToRadians(angle))
            }
        ]
    } else {
        rect = new Rect({
            left: -(radiusSmall),
            top: 0,
            fill: 'lightblue',
            width: straightTrackLength,
            height: 20,
            angle: 90
        });
        ends = []
    }
    // switch_id 
    const switchIDObj = currentIDs.newSwitchID()
    rect.id = switchIDObj.mainTrackID
    largeArc.id = switchIDObj.sideTrackIDLarge
    smallArc.id = switchIDObj.sideTrackIDSmall

    const group = new Group([largeArc, smallArc, rect], {
        left: 300,
        top: 200,
        angle: 0
    });
    group.id = switchIDObj.switchID
    group.state = "main"
    // 初始化道岔的颜色
    handleSwitchChange(canvas, group.id, group)

    group.lockScalingX = true;
    group.lockScalingY = true;
    group.lockScalingFlip = true;
    console.log("ends", side, ends)
    group.ends = ends
    canvas.add(group);
    // canvas.getObjects()
}

const createArcElements = (canvas: Canvas,
    angle: number,
    radius: number, // 内半径
    side: 'right' | 'left', center: { x: number, y: number }): { largeArc: Path, smallArc: Path } => {
    const radiusLarge = radius + trackWidth; // 较大扇形的半径
    const radiusSmall = radius; // 较小扇形的半径
    const centerX = center.x;
    const centerY = center.y;
    // angle = side === 'right' ? angle : - angle;
    // 创建较大扇形
    const largeArc = new Path(
        `M ${centerX},${centerY} ` +
        `L ${centerX + radiusLarge * Math.cos(util.degreesToRadians(0))},${centerY + radiusLarge * Math.sin(util.degreesToRadians(0))} ` +
        `A ${radiusLarge},${radiusLarge},0,0,1,${centerX + radiusLarge * Math.cos(util.degreesToRadians(angle))},${centerY + radiusLarge * Math.sin(util.degreesToRadians(angle))} ` +
        `z`,
        { fill: 'lightblue' }
    );

    // 创建较小扇形
    const smallArc = new Path(
        `M ${centerX},${centerY} ` +
        `L ${centerX + radiusSmall * Math.cos(util.degreesToRadians(0))},${centerY + radiusSmall * Math.sin(util.degreesToRadians(0))} ` +
        `A ${radiusSmall},${radiusSmall},0,0,1,${centerX + radiusSmall * Math.cos(util.degreesToRadians(angle))},${centerY + radiusSmall * Math.sin(util.degreesToRadians(angle))} ` +
        `z`,
        { fill: '#ffffff', stroke: '#888888' }
    );

    if (side == "left") {
        largeArc.set({ angle: -angle })
        smallArc.set({ angle: -angle })
    } else {
        // Do nothing
    }
    return { largeArc, smallArc }
}

const createArcTrack = (canvas: Canvas, startAngle: number, endAngle: number) => {
    const radiusLarge = 100; // 较大扇形的半径
    const radiusSmall = 80; // 较小扇形的半径
    const centerX = canvas.getWidth() / 2;
    const centerY = canvas.getHeight() / 2;

    const { largeArc, smallArc } = createArcElements(canvas, endAngle, radiusSmall, "right", { x: centerX, y: centerY });
    const group = new Group([largeArc, smallArc], {
        left: centerX,
        top: centerY,
        angle: 0
    });
    group.lockScalingX = true;
    group.lockScalingY = true;
    group.lockScalingFlip = true;
    const centerLineRadius = (radiusLarge + radiusSmall) / 2
    group.ends = [{
        heading: 0,
        dx: centerLineRadius,
        dy: 0
    }, {
        heading: normalizeAngle(180 + endAngle),
        dy: Math.sin(util.degreesToRadians(endAngle)) * centerLineRadius,
        dx: Math.cos(util.degreesToRadians(endAngle)) * centerLineRadius
    }]
    console.log(group.ends)

    // 将图形添加到画布上
    canvas.add(group);
}
const createStraightTrack = (canvas: Canvas) => {
    var rect = new Rect({
        left: 100,
        top: 100,
        fill: 'lightblue',
        width: 100,
        height: 20
    });
    rect.ends = [{ heading: 270, dx: 0, dy: 10 }, { heading: 90, dx: 100, dy: 10 }]

    // "add" rectangle onto canvas
    canvas.add(rect);
}

const showMenu = (x: number, y: number) => {
    menuStyle.value.visible = true
    menuStyle.value.top = y
    menuStyle.value.left = x
}

const hideMenu = () => {
    menuStyle.value.visible = false
    console.log("hide menu called!")
}

const delEl = () => {
    console.log("delel called!", contextMenuTarget)
    if (contextMenuTarget != null) {
        canvas!.remove(contextMenuTarget as FabricObject)
    }
    hideMenu()
}

// 遍历所有的对象
function traverseAllObjects(canvas: Canvas | Group, callback: (obj: FabricObject) => void): void {
    //   var allObjects = [];

    function traverseObjects(objects: FabricObject[]) {
        objects.forEach(function (obj) {
            // 将当前对象添加到数组中
            callback(obj)
            // 如果对象是一个组，递归地遍历它的子对象
            if (obj instanceof Group) {
                traverseObjects(obj.getObjects());
            }
        });
    }

    // 开始遍历canvas的顶级对象
    traverseObjects(canvas.getObjects());
    return;
    // return allObjects;
}

// 处理道岔切换样式事件
const handleSwitchChange = (canvas: Canvas, switchID: string, obj: Group): void => {
    const switchIDGroup = currentIDs.getSwitchIDGroup(switchID)
    const mainTrack = obj.getObjects().filter((obj: FabricObject) => obj.id && obj.id == switchIDGroup.mainTrackID)[0]
    const largeTrack = obj.getObjects().filter((obj: FabricObject) => obj.id && obj.id == switchIDGroup.sideTrackIDLarge)[0]
    const smallTrack = obj.getObjects().filter((obj: FabricObject) => obj.id && obj.id == switchIDGroup.sideTrackIDSmall)[0]

    if (mainTrack && largeTrack && smallTrack) {
        if (obj.state == "main") {
            mainTrack.set({ fill: '#19e157' })
            largeTrack.set({ fill: 'lightblue' })
            obj.state = "side"
            obj.bringObjectToFront(mainTrack)
            obj.sendObjectToBack(smallTrack)
            obj.sendObjectToBack(largeTrack)
            // canvas.moveObjectTo(sideTrack, 10)
        } else {
            mainTrack.set({ fill: 'lightblue' })
            largeTrack.set({ fill: '#19e157' })
            obj.state = "main"
            obj.bringObjectToFront(largeTrack)
            obj.bringObjectToFront(smallTrack)
            obj.sendObjectToBack(mainTrack)
        }
    } else {
        console.error("main or side track not found in switch", 'main', mainTrack, 'side-large', largeTrack, 'side-small', smallTrack)
    }
}

onMounted(() => {


    canvas = new Canvas('c', {
        fireRightClick: true, // 启用右键，button的数字为3
        stopContextMenu: true, // 禁止默认右键菜单
    });
    // 移动
    // canvas.on('mouse:down', function (options) {
    //     // viewportTransform = canvas.viewportTransform;
    //     // zoom = canvas.getZoom();
    //     if (canvas != null) {
    //         var pointer = canvas.getPointer(options.e);
    //         var x = pointer.x;
    //         var y = pointer.y;

    //         // 在控制台输出坐标
    //         console.log('点击的坐标是: (' + x + ', ' + y + ')');
    //     }
    // });
    canvas.setDimensions({ width: 1200, height: 800 });
    initAligningGuidelines(canvas, (e) => {
        console.log(e)
        if (e.button === 2 && e.target) {
            // 获取当前元素
            contextMenuTarget = e.target
            if (menu.value == null) {
                console.error('menu is null')
                return
            }
            if (canvas == null) {
                console.error('canvas is null')
                return
            }

            // 显示菜单，设置右键菜单位置
            // 获取菜单组件的宽高
            const menuWidth = menu.value.offsetWidth
            const menuHeight = menu.value.offsetHeight

            // 当前鼠标位置
            let pointX = e.x
            let pointY = e.y

            // 计算菜单出现的位置
            // 如果鼠标靠近画布右侧，菜单就出现在鼠标指针左侧
            if (canvas.width - pointX <= menuWidth) {
                pointX -= menuWidth
            }
            // 如果鼠标靠近画布底部，菜单就出现在鼠标指针上方
            if (canvas.height - pointY <= menuHeight) {
                pointY -= menuHeight
            }
            showMenu(pointX, pointY)
        } else {
            hideMenu()
        }
        if (e.target) {
            // 如果点击的是道岔
            if (e.target.id && e.target.id.startsWith("switch-")) {
                handleSwitchChange(canvas!, e.target.id, e.target as Group)
            }
        }
    }

    )
    // create a rectangle object
    createStraightTrack(canvas)
    createStraightTrack(canvas)
    // createSector(canvas)
    createArcTrack(canvas, 0, 45)
    // createSwitch(canvas, 30, 200, 180, "right")
    createSwitch(canvas, 30, 200, 180, "left")
    console.log(traverseAllObjects(canvas, (obj) => { console.log(obj) }))
})

</script>
<style>
/* 容器，相对定位 */
.box {
    position: relative;
}

/* 画布，给个边框 */
#canvas {
    border: 1px solid #ccc;
}

/* 菜单 */
.menu-x {
    visibility: hidden;
    /* 隐藏菜单 */
    z-index: -100;
    position: absolute;
    top: 0;
    left: 0;
    box-sizing: border-box;
    border-radius: 4px;
    box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
    background-color: #fff;
}

/* 菜单每个选项 */
.menu-li {
    box-sizing: border-box;
    padding: 4px 8px;
    border-bottom: 1px solid #ccc;
    cursor: pointer;
}

/* 鼠标经过的选项，更改背景色 */
.menu-li:hover {
    background-color: antiquewhite;
}

/* 第一个选项，顶部两角是圆角 */
.menu-li:first-child {
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
}

/* 最后一个选项，底部两角是圆角，底部不需要边框 */
.menu-li:last-child {
    border-bottom: none;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
}
</style>
